#define LIBTELNET_IMPLEMENTATION
#include "libtelnet.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h> // wait this is for gcc

/* 平台兼容性定义 */
#ifdef _WIN32
// Windows平台使用原生线程API
#include <conio.h>
#include <windows.h>
typedef HANDLE ThreadHandle;
#else
// Unix-like平台使用pthread
#include <termios.h>
#include <poll.h>
#include <pthread.h>
typedef pthread_t ThreadHandle;
#endif

/* 全局变量 */
telnet_session* g_session = NULL;
#ifdef __unix__
struct termios g_old_termios;
#endif
int g_running = 1;
char g_username[21] = {0};  // 用户名（最大20字符）
ThreadHandle g_input_thread;   // 输入处理线程

/* 清理函数 */
void cleanup(void) {
    g_running = 0;
    
    if (g_session) {
        telnet_disconnect(g_session);
        telnet_destroy(g_session);
        g_session = NULL;
    }

#ifdef __unix__
    /* 恢复终端设置 */
    tcsetattr(STDIN_FILENO, TCSANOW, &g_old_termios);
#else
    /* 恢复Windows控制台模式 */
    HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
    DWORD mode;
    if (GetConsoleMode(hStdin, &mode)) {
        mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT;
        SetConsoleMode(hStdin, mode);
    }
#endif
}

/* 信号处理函数 */
void signal_handler(int signum) {
    if (signum == SIGINT || signum == SIGTERM) {
        printf("\nReceived exit signal. Exiting...\n");
        cleanup();
        exit(0);
    }
}

/* 事件处理回调函数 */
void handle_event(telnet_session* session, const telnet_event* event) {
    switch (event->type) {
        case TELNET_EVENT_CONNECTED:
            printf("Connected to chat server!\n");
            break;
            
        case TELNET_EVENT_DATA:
            /* 输出接收到的消息 */
            fwrite(event->event.data.data, 1, event->event.data.length, stdout);
            fflush(stdout);
            break;
            
        case TELNET_EVENT_DISCONNECTED:
            printf("\nDisconnected from server\n");
            cleanup();
            exit(0);
            break;
            
        case TELNET_EVENT_ERROR:
            printf("\nError: %s\n", event->event.error.message);
            cleanup();
            exit(1);
            break;
    }
}

/* 设置终端模式 */
void set_terminal_mode() {
#ifdef __unix__
    tcgetattr(STDIN_FILENO, &g_old_termios);
    struct termios new_termios = g_old_termios;
    
    /* 启用回显，禁用规范模式 */
    new_termios.c_lflag |= ECHO;       // 启用回显
    new_termios.c_lflag &= ~ICANON;    // 禁用规范模式
    new_termios.c_cc[VMIN] = 1;        // 至少读取1个字符
    new_termios.c_cc[VTIME] = 0;       // 不超时
    
    tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
#else
    /* Windows控制台设置 */
    HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
    DWORD mode;
    if (GetConsoleMode(hStdin, &mode)) {
        mode |= ENABLE_ECHO_INPUT;         // 启用回显
        mode &= ~ENABLE_LINE_INPUT;        // 禁用行输入（实时读取）
        SetConsoleMode(hStdin, mode);
    }
#endif
}

/* 输入处理线程函数 */
#ifdef _WIN32
// Windows线程函数（必须使用__stdcall调用约定）
DWORD WINAPI input_thread(LPVOID arg)
#else
// Unix线程函数
void* input_thread(void* arg)
#endif
{
    char input_buffer[1024];
    size_t buffer_pos = 0;
    
    while (g_running) {
#ifdef __unix__
        // Unix系统读取输入
        char c;
        ssize_t n = read(STDIN_FILENO, &c, 1);
        if (n <= 0) continue;
#else
        // Windows系统读取输入（检查按键）
        if (!_kbhit()) {
            Sleep(10);  // 短暂休眠，减少CPU占用
            continue;
        }
        char c = _getch();  // 读取字符
#endif

        // 处理退格键
        if (c == '\b' || c == 127) {  // \b是退格，127是DEL
            if (buffer_pos > 0) {
                buffer_pos--;
                printf("\b \b");  // 清除屏幕上的字符
                fflush(stdout);
            }
            continue;
        }
        
        // 处理回车键（发送消息）
        if (c == '\r' || c == '\n') {
            if (buffer_pos > 0) {
                input_buffer[buffer_pos] = '\n';  // 补充换行符
                buffer_pos++;
                
                // 发送消息到服务器
                if (telnet_send(g_session, input_buffer, buffer_pos) != 0) {
                    printf("\nFailed to send message!\n");
                }
                
                // 重置缓冲区
                buffer_pos = 0;
                memset(input_buffer, 0, sizeof(input_buffer));
            }
            printf("\n");  // 显示换行
            fflush(stdout);
            continue;
        }
        
        // 处理退出命令 (输入 ":q" 回车)
        if (buffer_pos == 0 && c == ':') {
            input_buffer[buffer_pos++] = c;
            printf("%c", c);  // 回显
            fflush(stdout);
            continue;
        }
        if (buffer_pos == 1 && input_buffer[0] == ':' && c == 'q') {
            input_buffer[buffer_pos++] = c;
            printf("%c", c);
            fflush(stdout);
            continue;
        }
        
        // 普通字符（添加到缓冲区并回显）
        if (buffer_pos < sizeof(input_buffer) - 2) {  // 留一个位置给换行符
            input_buffer[buffer_pos++] = c;
            printf("%c", c);  // 手动回显
            fflush(stdout);
        }
    }
    
#ifdef _WIN32
    return 0;  // Windows线程返回值
#else
    return NULL;
#endif
}

/* 读取用户名（带提示和验证） */
void get_username() {
    while (1) {
        printf("Enter your username (1-20 characters): ");
        fflush(stdout);
        
        if (fgets(g_username, sizeof(g_username), stdin)) {
            // 移除换行符
            size_t len = strlen(g_username);
            if (len > 0 && g_username[len - 1] == '\n') {
                g_username[len - 1] = '\0';
                len--;
            }
            
            // 验证用户名长度
            if (len >= 1 && len <= 20) {
                break;  // 用户名有效
            }
            printf("Username must be 1-20 characters!\n");
        }
    }
}

// 跨平台线程创建函数
int create_thread(ThreadHandle* thread, void* (*start_routine)(void*), void* arg) {
#ifdef _WIN32
    *thread = CreateThread(
        NULL,           // 默认安全属性
        0,              // 默认栈大小
        (LPTHREAD_START_ROUTINE)start_routine,  // 线程函数
        arg,            // 传递给线程函数的参数
        0,              // 立即运行线程
        NULL            // 不需要线程ID
    );
    return (*thread == NULL) ? -1 : 0;
#else
    return pthread_create(thread, NULL, start_routine, arg);
#endif
}

// 跨平台线程等待函数
int join_thread(ThreadHandle thread) {
#ifdef _WIN32
    WaitForSingleObject(thread, INFINITE);
    CloseHandle(thread);
    return 0;
#else
    return pthread_join(thread, NULL);
#endif
}

int main(int argc, char* argv[]) {
    const char* host = "localhost";
    uint16_t port = 8888;
    
    /* 解析命令行参数 */
    if (argc >= 2) {
        host = argv[1];
    }
    if (argc >= 3) {
        port = (uint16_t)atoi(argv[2]);
    }
    
    /* 获取用户名 */
    get_username();
    printf("Connecting to chat server %s:%d as %s...\n", host, port, g_username);
    
    /* 注册清理函数和信号处理 */
    atexit(cleanup);
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    /* 创建telnet会话 */
    g_session = telnet_create(4096);
    if (!g_session) {
        fprintf(stderr, "Error: Failed to create telnet session\n");
        return 1;
    }
    
    /* 连接到服务器 */
    if (telnet_connect(g_session, host, port) != 0) {
        fprintf(stderr, "Error: Failed to connect to %s:%d\n", host, port);
        return 1;
    }
    
    /* 发送用户名（带换行符，匹配服务器的接收逻辑） */
    char username_with_newline[256];
    snprintf(username_with_newline, sizeof(username_with_newline), "%s\n", g_username);
    if (telnet_send(g_session, username_with_newline, strlen(username_with_newline)) != 0) {
        fprintf(stderr, "Error: Failed to send username\n");
        cleanup();
        return 1;
    }
    
    /* 设置终端模式 */
    set_terminal_mode();
    
    /* 创建输入处理线程（跨平台实现） */
    if (create_thread(&g_input_thread, (void*(*)(void*))input_thread, NULL) != 0) {
        fprintf(stderr, "Error: Failed to create input thread\n");
        cleanup();
        return 1;
    }
    
    /* 主事件循环 */
    printf("Connected to chat server. Type messages to chat, :q to quit.\n");
    while (g_running && g_session->state == TELNET_STATE_CONNECTED) {
        telnet_process(g_session, handle_event);
        
#ifdef __unix__
        usleep(10000);  // 10ms休眠，减少CPU占用
#else
        Sleep(10);      // Windows下休眠
#endif
    }
    
    /* 等待输入线程结束 */
    join_thread(g_input_thread);
    
    return 0;
}
